#include "stdafx.h"
#include "layoutDesc.h"

//---------------------------------------------------------

CLayoutGroupElement::CLayoutGroupElement( const std::string& name, ELayoutVarType varType, const bool isOptional, const class CLayoutGroup* subGroup, bool isArray )
	: m_name( name )
	, m_type( varType )
	, m_hasDefaultValue( false )
	, m_isOptional( isOptional )
	, m_subGroup( subGroup )
	, m_isArray( isArray )
{
}

CLayoutGroupElement::~CLayoutGroupElement()
{
}

//---------------------------------------------------------

CLayoutGroup::CLayoutGroup( const std::string& name, const bool isMessage )
	: m_name( name )
	, m_baseGroup( nullptr )
	, m_defined( false )
	, m_valueType( eLayoutVar_Invalid )
	, m_isMessage( isMessage )
	, m_classIndex( -1 )
	, m_lastChildIndex( -1 )
{
}

CLayoutGroup::~CLayoutGroup()
{
	for ( auto* ptr : m_elements )
		delete ptr;
}

void CLayoutGroup::EnumChildGroups( std::vector<const CLayoutGroup*>& allChildGroups ) const
{
	allChildGroups.push_back( this );

	for ( auto* child : m_derivedGroups )
		child->EnumChildGroups( allChildGroups );
}

const CLayoutGroup* CLayoutGroup::FindChildGroup( const std::string& groupName ) const
{
	// test the current group first
	if ( groupName == m_name )
		return this;

	// try luck in the child groups
	for ( const auto* child : m_derivedGroups )
	{
		const auto* ret = child->FindChildGroup( groupName );
		if ( ret != nullptr )
			return ret;
	}

	// nothing found
	return nullptr;
}

bool CLayoutGroup::DerivesFrom( const CLayoutGroup* baseGroup ) const
{
	const CLayoutGroup* cur = this;
	while ( cur != nullptr )
	{
		if ( cur == baseGroup )
			return true;

		cur = cur->GetBaseGroup();
	}

	return false;
}

const CLayoutGroupElement* CLayoutGroup::GetElement( const std::string& name ) const
{
	for ( const auto* ptr : m_elements )
		if ( ptr->GetName() == name )
			return ptr;

	return nullptr;
}

void CLayoutGroup::GetWritableElements( std::vector< const CLayoutGroupElement* >&outElems ) const
{
	if ( m_baseGroup )
		m_baseGroup->GetWritableElements( outElems );

	for ( const auto* ptr : m_elements )
		outElems.push_back( ptr );
}

//---------------------------------------------------------

CLayoutDefinition::CLayoutDefinition()
{
}

CLayoutDefinition::~CLayoutDefinition()
{
	for ( auto* ptr : m_groups )
		delete ptr;
}

const CLayoutGroup* CLayoutDefinition::FindGroup( const std::string& groupName )
{
	// return existing
	for ( CLayoutGroup* group : m_groups )
		if ( group->GetName() == groupName )
			return group;

	// not found
	return nullptr;
}

CLayoutGroup* CLayoutDefinition::GetGroup( const std::string& groupName, const bool isMessage )
{
	// return existing
	for ( CLayoutGroup* group : m_groups )
		if ( group->GetName() == groupName )
		{
			if ( group->IsMessage() && !isMessage )
			{
				fprintf( stderr, "Group %s first defined as a message now as a struct\n", groupName.c_str() );
				return nullptr;
			}
			else if ( !group->IsMessage() && isMessage )
			{
				fprintf( stderr, "Group %s first defined as a struct now as a message\n", groupName.c_str() );
				return nullptr;
			}

			return group;
		}

	// create new
	CLayoutGroup* group = new CLayoutGroup( groupName, isMessage );
	m_groups.push_back( group );
	return group;
}

bool CLayoutDefinition::Validate() const
{
	bool noErrors = true;

	// make sure all groups are defined
	for ( const CLayoutGroup* group : m_groups )
	{
		if ( !group->IsDefined() )
		{
			fprintf( stderr, "%s '%s' was referenced but not defined\n",
				group->IsMessage() ? "Message" : "Struct",
				group->GetName().c_str() );

			noErrors = false;
		}
	}

	return noErrors;
}

//---------------------------------------------------------
